﻿using Acr.UserDialogs;
using FormsVideoLibrary;
using GMap.NET;
using GMap.NET.MapProviders;
using GMap.NET.WindowsForms;
using GMap.NET.WindowsForms.Markers;
using log4net;
using MissionPlanner;
using MissionPlanner.ArduPilot;
using MissionPlanner.Controls;
using MissionPlanner.Maps;
using MissionPlanner.Utilities;
using MissionPlanner.Warnings;
using Plugin.FilePicker;
using Plugin.FilePicker.Abstractions;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Xamarin.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Button = Xamarin.Forms.Button;
using Color = System.Drawing.Color;
using Device = Xamarin.Forms.Device;
using Exception = System.Exception;
using Label = Xamarin.Forms.Label;


namespace Xamarin
{
    public partial class FlightData : ContentPage, IActivate, IDeactivate
    {
        public static FlightData instance;
        public static GMapOverlay kmlpolygons;
        public static HUD myhud;
        public static GMapControl mymap;
        public static bool threadrun;
        internal static GMapOverlay geofence;
        internal static GMapOverlay photosoverlay;
        internal static GMapOverlay poioverlay = new GMapOverlay("POI");
        internal static GMapOverlay rallypointoverlay;
        internal static GMapOverlay tfrpolygons;
        internal GMapMarker CurrentGMapMarker;
        internal PointLatLng MouseDownStart;
        private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private bool CameraOverlap;
        GMapMarker center = new GMarkerGoogle(new PointLatLng(0.0, 0.0), GMarkerGoogleType.none);

        /// <summary>
        /// Try to reduce the number of map position changes generated by the code
        /// </summary>
        DateTime lastmapposchange = DateTime.MinValue;

        GMapMarker marker;

        GMapOverlay polygons;
        private Propagation prop;
        Random random = new Random();
        GMapRoute route;
        GMapOverlay routes;
        Thread thisthread;
        SKPoint touchpoint = new SKPoint();

        public FlightData()
        {
            log.Info("Ctor Start");

            InitializeComponent();

            log.Info("Components Done");

            instance = this;
            mymap = gMapControl1;
            myhud = hud1;

            switch (Forms.Device.RuntimePlatform)
            {
                case Device.Android:
                    //myhud.IgnorePixelScaling = true;
                    break;
            }

            if (!string.IsNullOrEmpty(Settings.Instance["hudcolor"]))
            {
                hud1.hudcolor = Color.FromName(Settings.Instance["hudcolor"]);
            }

            List<string> list = new List<string>();

            {
                list.Add("LOITER_UNLIM");
                list.Add("RETURN_TO_LAUNCH");
                list.Add("PREFLIGHT_CALIBRATION");
                list.Add("MISSION_START");
                list.Add("PREFLIGHT_REBOOT_SHUTDOWN");
                list.Add("Trigger Camera NOW");
                list.Add("SYSTEM_TIME");
                //DO_SET_SERVO
                //DO_REPEAT_SERVO
            }

            GMap.NET.GMaps.Instance.PrimaryCache = new MissionPlanner.Maps.MyImageCache();

            gMapControl1.LevelsKeepInMemmory = 10;
            //gMapControl1.Manager.MemoryCache.Size

            gMapControl1.MapProvider = GMapProviders.GoogleSatelliteMap;

            gMapControl1.MapScaleInfoEnabled = true;
            gMapControl1.ScalePen = new Pen(Color.White);
            gMapControl1.Position = new PointLatLng(0, 0);

            this.gMapControl1.OnPositionChanged += new GMap.NET.PositionChanged(this.gMapControl1_OnPositionChanged);
            // this.gMapControl1.Click += new System.EventHandler(this.gMapControl1_Click);
            this.gMapControl1.MouseDown += this.gMapControl1_MouseDown;
            this.gMapControl1.MouseLeave += this.gMapControl1_MouseLeave;
            this.gMapControl1.MouseMove += this.gMapControl1_MouseMove;

            //gMapControl1.ShowTileGridLines = true;

            // config map      
            log.Info("Map Setup");
            gMapControl1.CacheLocation = Settings.GetDataDirectory() +
                                         "gmapcache" + Path.DirectorySeparatorChar;
            gMapControl1.MaxZoom = 24;
            gMapControl1.MinZoom = 1;
            gMapControl1.Zoom = 3;

            gMapControl1.ScaleMode = ScaleModes.Fractional;
            gMapControl1.LevelsKeepInMemmory = 5;

            gMapControl1.OnMapZoomChanged += gMapControl1_OnMapZoomChanged;

            gMapControl1.DisableFocusOnMouseEnter = true;

            gMapControl1.OnMarkerEnter += gMapControl1_OnMarkerEnter;
            gMapControl1.OnMarkerLeave += gMapControl1_OnMarkerLeave;

            gMapControl1.RoutesEnabled = true;
            gMapControl1.PolygonsEnabled = true;

            tfrpolygons = new GMapOverlay("tfrpolygons");
            gMapControl1.Overlays.Add(tfrpolygons);

            kmlpolygons = new GMapOverlay("kmlpolygons");
            gMapControl1.Overlays.Add(kmlpolygons);

            geofence = new GMapOverlay("geofence");
            gMapControl1.Overlays.Add(geofence);

            polygons = new GMapOverlay("polygons");
            gMapControl1.Overlays.Add(polygons);

            photosoverlay = new GMapOverlay("photos overlay");
            gMapControl1.Overlays.Add(photosoverlay);

            routes = new GMapOverlay("routes");
            gMapControl1.Overlays.Add(routes);

            rallypointoverlay = new GMapOverlay("rally points");
            gMapControl1.Overlays.Add(rallypointoverlay);

            gMapControl1.Overlays.Add(poioverlay);

            FlightData_Load(null, null);

            Activate();
        }

        public void Activate()
        {
            log.Info("Activate Called");

            hud1.altunit = CurrentState.AltUnit;
            hud1.speedunit = CurrentState.SpeedUnit;
            hud1.distunit = CurrentState.DistanceUnit;

            Mode.Items.AddRange(MissionPlanner.ArduPilot.Common.getModesList(MainV2.comPort.MAV.cs.firmware)
                .Select(a => a.Value));

            CheckBatteryShow();

            // make sure the hud user items/warnings/checklist are using the current state
            HUD.Custom.src = MainV2.comPort.MAV.cs;
            CustomWarning.defaultsrc = MainV2.comPort.MAV.cs;


            if (Settings.Instance["maplast_lat"] != "")
            {
                try
                {
                    gMapControl1.Position = new PointLatLng(Settings.Instance.GetDouble("maplast_lat"),
                        Settings.Instance.GetDouble("maplast_lng"));
                    if (Math.Round(Settings.Instance.GetDouble("maplast_lat"), 1) == 0)
                    {
                        // no zoom in

                    }
                    else
                    {
                        var zoom = Settings.Instance.GetFloat("maplast_zoom");

                    }
                }
                catch
                {
                }
            }

            //videoPlayer.Source = VideoSource.FromUri("rtsp://192.168.0.10:8554/H264Video");

            //videoPlayer.Play();
        }

        public void BUT_playlog_Click(object sender, EventArgs e)
        {
            if (MainV2.comPort.logreadmode)
            {
                MainV2.comPort.logreadmode = false;

                playingLog = false;
            }
            else
            {
                // BUT_clear_track_Click(sender, e);
                MainV2.comPort.logreadmode = true;

                playingLog = true;
            }
        }

        public void CheckBatteryShow()
        {
            // ensure battery display is on - also set in hud if current is updated
            if (MainV2.comPort.MAV.param.ContainsKey("BATT_MONITOR") &&
                (float) MainV2.comPort.MAV.param["BATT_MONITOR"] != 0)
            {
                hud1.batteryon = true;
            }
            else
            {
                hud1.batteryon = false;
            }
        }

        public void Deactivate()
        {

            Settings.Instance["maplast_lat"] = gMapControl1.Position.Lat.ToString();
            Settings.Instance["maplast_lng"] = gMapControl1.Position.Lng.ToString();
            Settings.Instance["maplast_zoom"] = gMapControl1.Zoom.ToString();

        }

        public void LoadLogFile(FileData file)
        {
            if (file != null)
            {
                try
                {
                    BUT_clear_track_Click(null, null);

                    MainV2.comPort.logreadmode = true;
                    MainV2.comPort.logplaybackfile = new BinaryReader(file.GetStream());
                    MainV2.comPort.lastlogread = DateTime.MinValue;

                    LBL_logfn.Text = Path.GetFileName(file.FileName);

                    log.Info("Open logfile " + file);

                    MainV2.comPort.getHeartBeat();

                    tracklog.Value = 0;
                    tracklog.Minimum = 0;
                    tracklog.Maximum = 100;
                }
                catch (Exception ex)
                {
                    CustomMessageBox.Show(Strings.PleaseLoadValidFile + ex.ToString(), Strings.ERROR);
                }
            }
        }

        public void Invoke(Action action)
        {
            Forms.Device.BeginInvokeOnMainThread(action);
        }

        protected void Dispose(bool disposing)
        {
            MainV2.comPort.logreadmode = false;
            try
            {
                //if (hud1 != null)
                //Settings.Instance["FlightSplitter"] = MainH.SplitterDistance.ToString();
            }
            catch
            {
            }

            if (polygons != null)
                polygons.Dispose();
            if (routes != null)
                routes.Dispose();
            if (route != null)
                route.Dispose();
            if (marker != null)
                marker.Dispose();

            if (prop != null)
                prop.Stop();
        }

        void addHudUserItem(ref HUD.Custom cust, string name)
        {
            setupPropertyInfo(ref cust.Item, name, MainV2.comPort.MAV.cs);

            hud1.CustomItems[name] = cust;

            hud1.Invalidate();
        }

        private void addMissionPhotoMarker(GMapMarker marker)
        {
            // not async
            Invoke((Action) delegate { photosoverlay.Markers.Add(marker); });
        }

        private void addMissionRouteMarker(GMapMarker marker)
        {
            // not async
            Invoke((Action) delegate { routes.Markers.Add(marker); });
        }


        private void addpolygonmarker(string tag, double lng, double lat, int alt, Color? color, GMapOverlay overlay)
        {
            try
            {
                PointLatLng point = new PointLatLng(lat, lng);
                GMarkerGoogle m = new GMarkerGoogle(point, GMarkerGoogleType.green);
                m.ToolTipMode = MarkerTooltipMode.Always;
                m.ToolTipText = tag;
                m.Tag = tag;

                GMapMarkerRect mBorders = new GMapMarkerRect(point);
                {
                    mBorders.InnerMarker = m;
                    try
                    {
                        mBorders.wprad =
                            (int) (Settings.Instance.GetFloat("TXT_WPRad") / CurrentState.multiplierdist);
                    }
                    catch
                    {
                    }

                    if (color.HasValue)
                    {
                        mBorders.Color = color.Value;
                    }
                }

                Invoke((Action) delegate
                {
                    overlay.Markers.Add(m);
                    overlay.Markers.Add(mBorders);
                });
            }
            catch (Exception)
            {
            }
        }

        private void addpolygonmarkerred(string tag, double lng, double lat, int alt, Color? color, GMapOverlay overlay)
        {
            try
            {
                PointLatLng point = new PointLatLng(lat, lng);
                GMarkerGoogle m = new GMarkerGoogle(point, GMarkerGoogleType.red);
                m.ToolTipMode = MarkerTooltipMode.Always;
                m.ToolTipText = tag;
                m.Tag = tag;

                GMapMarkerRect mBorders = new GMapMarkerRect(point);
                {
                    mBorders.InnerMarker = m;
                }

                Invoke((Action) delegate
                {
                    overlay.Markers.Add(m);
                    overlay.Markers.Add(mBorders);
                });
            }
            catch (Exception)
            {
            }
        }

        private void BUT_clear_track_Click(object sender, EventArgs e)
        {
            if (route != null)
                route.Points.Clear();

            if (MainV2.comPort.MAV.camerapoints != null)
                MainV2.comPort.MAV.camerapoints.Clear();
        }

        private async void BUT_loadtelem_Click(object sender, EventArgs e)
        {
            LBL_logfn.Text = "";

            if (MainV2.comPort.logplaybackfile != null)
            {
                try
                {
                    MainV2.comPort.logplaybackfile.Close();
                    MainV2.comPort.logplaybackfile = null;
                }
                catch
                {
                }
            }



            //using (OpenFileDialog fd = new OpenFileDialog())
            {
                //fd.AddExtension = true;
                //fd.Filter = "Telemetry log (*.tlog)|*.tlog;*.tlog.*|Mavlink Log (*.mavlog)|*.mavlog";
                //fd.InitialDirectory = Settings.Instance.LogDir;
                //fd.DefaultExt = ".tlog";
                //DialogResult result = fd.ShowDialog();

                FileData file = await CrossFilePicker.Current.PickFile(new string[] {".tlog"});
                if (file == null)
                    return; // user canceled file picking

                LoadLogFile(file);
            }
        }

        private void BUT_log2kml_Click(object sender, EventArgs e)
        {
            //Form frm = new MavlinkLog();
            //ThemeManager.ApplyThemeTo(frm);
            //frm.Show();
        }

        private void BUT_quickauto_Click(object sender, EventArgs e)
        {
            try
            {
                ((Button) sender).IsEnabled = false;
                MainV2.comPort.setMode("Auto");
            }
            catch
            {
                CustomMessageBox.Show(Strings.CommandFailed, Strings.ERROR);
            }

            ((Button) sender).IsEnabled = true;
        }

        private void BUT_quickmanual_Click(object sender, EventArgs e)
        {
            try
            {
                ((Button) sender).IsEnabled = false;
                if (MainV2.comPort.MAV.cs.firmware == Firmwares.ArduPlane ||
                    MainV2.comPort.MAV.cs.firmware == Firmwares.Ateryx ||
                    MainV2.comPort.MAV.cs.firmware == Firmwares.ArduRover)
                    MainV2.comPort.setMode("Loiter");
                if (MainV2.comPort.MAV.cs.firmware == Firmwares.ArduCopter2)
                    MainV2.comPort.setMode("Loiter");
            }
            catch
            {
                CustomMessageBox.Show(Strings.CommandFailed, Strings.ERROR);
            }

            ((Button) sender).IsEnabled = true;
        }

        private void BUT_quickrtl_Click(object sender, EventArgs e)
        {
            try
            {
                ((Button) sender).IsEnabled = false;
                MainV2.comPort.setMode("RTL");
            }
            catch
            {
                CustomMessageBox.Show(Strings.CommandFailed, Strings.ERROR);
            }

            ((Button) sender).IsEnabled = true;
        }

        void cam_camimage(System.Drawing.Image camimage)
        {
            hud1.bgimage = camimage;
        }

        private void CheckAndBindPreFlightData()
        {
            //this.Invoke((Action) delegate { preFlightChecklist1.BindData(); });
        }

 

        private void FlightData_Load(object sender, EventArgs e)
        {
            //POI.POIModified += POI_POIModified;

            tfr.GotTFRs += tfr_GotTFRs;

            //if (!Settings.Instance.ContainsKey("ShowNoFly") || Settings.Instance.GetBoolean("ShowNoFly"))
            //NoFly.NoFly.NoFlyEvent += NoFly_NoFlyEvent;

  

            gMapControl1.EmptyTileColor = Color.Gray;

            //Zoomlevel.Minimum = gMapControl1.MapProvider.MinZoom;
            //Zoomlevel.Maximum = 24;
            //Zoomlevel.Value = Convert.ToDecimal(gMapControl1.Zoom);

            var item1 = ParameterMetaDataRepository.GetParameterOptionsInt("MNT_MODE",
                MainV2.comPort.MAV.cs.firmware.ToString());
            var item2 = ParameterMetaDataRepository.GetParameterOptionsInt("MNT_DEFLT_MODE",
                MainV2.comPort.MAV.cs.firmware.ToString());
            //if (item1.Count > 0)
            //CMB_mountmode.DataSource = item1;

            //if (item2.Count > 0)
            //CMB_mountmode.DataSource = item2;

            //CMB_mountmode.DisplayMember = "Value";
            //CMB_mountmode.ValueMember = "Key";



            //if (Settings.Instance.ContainsKey("HudSwap") && Settings.Instance["HudSwap"] == "true")
            //SwapHud1AndMap();

            if (Settings.Instance.ContainsKey("FlightSplitter"))
            {
                //MainH.SplitterDistance = Settings.Instance.GetInt32("FlightSplitter");
            }

            if (Settings.Instance.ContainsKey("russian_hud"))
            {
                hud1.Russian = Settings.Instance.GetBoolean("russian_hud");
            }

            //groundColorToolStripMenuItem.Checked = Settings.Instance.GetBoolean("groundColorToolStripMenuItem");
            //groundColorToolStripMenuItem_Click(null, null);

            hud1.doResize();

            prop = new Propagation(gMapControl1);

            thisthread = new Thread(mainloop);
            thisthread.Name = "FD Mainloop";
            thisthread.IsBackground = true;
            thisthread.Start();
        }

        private void gMapControl1_MouseDown(object sender, MouseEventArgs e)
        {
            MouseDownStart = gMapControl1.FromLocalToLatLng(e.X, e.Y);


            if (gMapControl1.IsMouseOverMarker)
            {
                if (CurrentGMapMarker is GMapMarkerADSBPlane)
                {
                    var marker = CurrentGMapMarker as GMapMarkerADSBPlane;
                    if (marker.Tag is adsb.PointLatLngAltHdg)
                    {
                        var plla = marker.Tag as adsb.PointLatLngAltHdg;
                        plla.DisplayICAO = !plla.DisplayICAO;
                    }
                }
            }
        }

        private void gMapControl1_MouseLeave(object sender, EventArgs e)
        {
            if (marker != null)
            {
                try
                {
                    if (routes.Markers.Contains(marker))
                        routes.Markers.Remove(marker);
                }
                catch
                {
                }
            }
        }

        private void gMapControl1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                PointLatLng point = gMapControl1.FromLocalToLatLng(e.X, e.Y);

                double latdif = MouseDownStart.Lat - point.Lat;
                double lngdif = MouseDownStart.Lng - point.Lng;

                gMapControl1.Position = new PointLatLng(center.Position.Lat + latdif,
                    center.Position.Lng + lngdif);
            }
            else
            {
                // setup a ballon with home distance
                if (marker != null)
                {
                    if (routes.Markers.Contains(marker))
                        routes.Markers.Remove(marker);
                }

                if (Settings.Instance.GetBoolean("CHK_disttohomeflightdata") != false)
                {
                    PointLatLng point = gMapControl1.FromLocalToLatLng(e.X, e.Y);

                    marker = new GMapMarkerRect(point);
                    marker.ToolTip = new GMapToolTip(marker);
                    marker.ToolTipMode = MarkerTooltipMode.Always;
                    marker.ToolTipText = "Dist to Home: " +
                                         ((gMapControl1.MapProvider.Projection.GetDistance(point,
                                               MainV2.comPort.MAV.cs.HomeLocation.Point()) * 1000) *
                                          CurrentState.multiplierdist).ToString("0");

                    routes.Markers.Add(marker);
                }
            }
        }

        void gMapControl1_OnMapZoomChanged()
        {
            try
            {
                // Exception System.Runtime.InteropServices.SEHException: External component has thrown an exception.
       
                //  Zoomlevel.Value = Convert.ToDecimal(gMapControl1.Zoom);
            }
            catch
            {
            }

            center.Position = gMapControl1.Position;
        }

        void gMapControl1_OnMarkerEnter(GMapMarker item)
        {
            CurrentGMapMarker = item;
        }

        void gMapControl1_OnMarkerLeave(GMapMarker item)
        {
            CurrentGMapMarker = null;
        }

        private void gMapControl1_OnPositionChanged(PointLatLng point)
        {
            center.Position = point;

            UpdateOverlayVisibility();
        }

        double LogPlayBackSpeed = 1.0;
        bool playingLog;
        List<PointLatLng> trackPoints = new List<PointLatLng>();

        private async void mainloop()
        {
            if (threadrun == true)
                return;
            threadrun = true;
            EndPoint Remote = new IPEndPoint(IPAddress.Any, 0);

            DateTime tracklast = DateTime.Now.AddSeconds(0);

            DateTime tunning = DateTime.Now.AddSeconds(0);

            DateTime mapupdate = DateTime.Now.AddSeconds(0);

            DateTime vidrec = DateTime.Now.AddSeconds(0);

            DateTime waypoints = DateTime.Now.AddSeconds(0);

            DateTime updatescreen = DateTime.Now;

            DateTime tsreal = DateTime.Now;
            double taketime = 0;
            double timeerror = 0;


            while (threadrun)
            {
                if (MainV2.comPort.giveComport)
                {
                    Thread.Sleep(50);
                    updateBindingSource();
                    continue;
                }

                if (!MainV2.comPort.logreadmode)
                    Thread.Sleep(50); // max is only ever 10 hz but we go a little faster to empty the serial queue


                // log playback
                if (MainV2.comPort.logreadmode && MainV2.comPort.logplaybackfile != null)
                {
                    if (MainV2.comPort.BaseStream.IsOpen)
                    {
                        MainV2.comPort.logreadmode = false;
                        try
                        {
                            MainV2.comPort.logplaybackfile.Close();
                        }
                        catch
                        {
                            log.Error("Failed to close logfile");
                        }

                        MainV2.comPort.logplaybackfile = null;
                    }


                    //Console.WriteLine(DateTime.Now.Millisecond);

                    if (updatescreen.AddMilliseconds(300) < DateTime.Now)
                    {
                        try
                        {
                            updatePlayPauseButton(true);
                            updateLogPlayPosition();
                        }
                        catch
                        {
                            log.Error("Failed to update log playback pos");
                        }

                        updatescreen = DateTime.Now;
                    }

                    //Console.WriteLine(DateTime.Now.Millisecond + " done ");

                    DateTime logplayback = MainV2.comPort.lastlogread;
                    try
                    {
                        if (!MainV2.comPort.giveComport)
                            await MainV2.comPort.readPacketAsync().ConfigureAwait(false);

                        // update currentstate of sysids on the port
                        foreach (var MAV in MainV2.comPort.MAVlist)
                        {
                            try
                            {
                                MAV.cs.UpdateCurrentSettings(null, false, MainV2.comPort, MAV);
                            }
                            catch (Exception ex)
                            {
                                log.Error(ex);
                            }
                        }
                    }
                    catch
                    {
                        log.Error("Failed to read log packet");
                    }

                    double act = (MainV2.comPort.lastlogread - logplayback).TotalMilliseconds;

                    if (act > 9999 || act < 0)
                        act = 0;

                    double ts = 0;
                    if (LogPlayBackSpeed == 0)
                        LogPlayBackSpeed = 0.01;
                    try
                    {
                        ts = Math.Min((act / LogPlayBackSpeed), 1000);
                    }
                    catch
                    {
                    }

                    if (LogPlayBackSpeed >= 4 && MainV2.speechEnabled())
                        MainV2.speechEngine.SpeakAsyncCancelAll();

                    double timetook = (DateTime.Now - tsreal).TotalMilliseconds;
                    if (timetook != 0)
                    {
                        //Console.WriteLine("took: " + timetook + "=" + taketime + " " + (taketime - timetook) + " " + ts);
                        //Console.WriteLine(MainV2.comPort.lastlogread.Second + " " + DateTime.Now.Second + " " + (MainV2.comPort.lastlogread.Second - DateTime.Now.Second));
                        //if ((taketime - timetook) < 0)
                        {
                            timeerror += (taketime - timetook);
                            if (ts != 0)
                            {
                                ts += timeerror;
                                timeerror = 0;
                            }
                        }
                        if (Math.Abs(ts) > 1000)
                            ts = 1000;
                    }

                    taketime = ts;
                    tsreal = DateTime.Now;

                    if (ts > 0 && ts < 1000)
                        Thread.Sleep((int) ts);

                    tracklast = tracklast.AddMilliseconds(ts - act);
                    tunning = tunning.AddMilliseconds(ts - act);

                    if (tracklast.Month != DateTime.Now.Month)
                    {
                        tracklast = DateTime.Now;
                        tunning = DateTime.Now;
                    }

                    try
                    {
                        if (MainV2.comPort.logplaybackfile != null &&
                            MainV2.comPort.logplaybackfile.BaseStream.Position ==
                            MainV2.comPort.logplaybackfile.BaseStream.Length)
                        {
                            MainV2.comPort.logreadmode = false;
                        }
                    }
                    catch
                    {
                        MainV2.comPort.logreadmode = false;
                    }
                }
                else
                {
                    // ensure we know to stop
                    if (MainV2.comPort.logreadmode)
                        MainV2.comPort.logreadmode = false;
                    updatePlayPauseButton(false);

                    if (!playingLog && MainV2.comPort.logplaybackfile != null)
                    {
                        continue;
                    }
                }

                try
                {
                    CheckAndBindPreFlightData();
                    //Console.WriteLine(DateTime.Now.Millisecond);
                    //int fixme;
                    updateBindingSource();
                    // Console.WriteLine(DateTime.Now.Millisecond + " done ");

                    // battery warning.
                    float warnvolt = Settings.Instance.GetFloat("speechbatteryvolt");
                    float warnpercent = Settings.Instance.GetFloat("speechbatterypercent");

                    if (MainV2.comPort.MAV.cs.battery_voltage <= warnvolt)
                    {
                        hud1.lowvoltagealert = true;
                    }
                    else if ((MainV2.comPort.MAV.cs.battery_remaining) < warnpercent)
                    {
                        hud1.lowvoltagealert = true;
                    }
                    else
                    {
                        hud1.lowvoltagealert = false;
                    }



                    Forms.Device.BeginInvokeOnMainThread(() =>
                    {
                        var start = DateTime.Now;

                        hud1.HoldInvalidation = true;
                        hud1.airspeed = MainV2.comPort.MAV.cs.airspeed;
                        hud1.alt = MainV2.comPort.MAV.cs.alt;
                        hud1.batterylevel = (float) MainV2.comPort.MAV.cs.battery_voltage;
                        hud1.batteryremaining = MainV2.comPort.MAV.cs.battery_remaining;
                        hud1.connected = MainV2.comPort.MAV.cs.connected;
                        hud1.current = (float) MainV2.comPort.MAV.cs.current;
                        hud1.datetime = MainV2.comPort.MAV.cs.datetime;
                        hud1.disttowp = MainV2.comPort.MAV.cs.wp_dist;
                        hud1.ekfstatus = MainV2.comPort.MAV.cs.ekfstatus;
                        hud1.failsafe = MainV2.comPort.MAV.cs.failsafe;
                        hud1.gpsfix = MainV2.comPort.MAV.cs.gpsstatus;
                        hud1.gpsfix2 = MainV2.comPort.MAV.cs.gpsstatus2;
                        hud1.gpshdop = MainV2.comPort.MAV.cs.gpshdop;
                        hud1.gpshdop2 = MainV2.comPort.MAV.cs.gpshdop2;
                        hud1.groundalt = (float) MainV2.comPort.MAV.cs.HomeAlt;
                        hud1.groundcourse = MainV2.comPort.MAV.cs.groundcourse;
                        hud1.groundspeed = MainV2.comPort.MAV.cs.groundspeed;
                        hud1.heading = MainV2.comPort.MAV.cs.yaw;
                        hud1.linkqualitygcs = MainV2.comPort.MAV.cs.linkqualitygcs;
                        hud1.message = MainV2.comPort.MAV.cs.messageHigh;
                        hud1.mode = MainV2.comPort.MAV.cs.mode;
                        hud1.navpitch = MainV2.comPort.MAV.cs.nav_pitch;
                        hud1.navroll = MainV2.comPort.MAV.cs.nav_roll;
                        hud1.pitch = MainV2.comPort.MAV.cs.pitch;
                        hud1.roll = MainV2.comPort.MAV.cs.roll;
                        hud1.status = MainV2.comPort.MAV.cs.armed;
                        hud1.targetalt = MainV2.comPort.MAV.cs.targetalt;
                        hud1.targetheading = MainV2.comPort.MAV.cs.nav_bearing;
                        hud1.targetspeed = MainV2.comPort.MAV.cs.targetairspeed;
                        hud1.turnrate = MainV2.comPort.MAV.cs.turnrate;
                        hud1.verticalspeed = MainV2.comPort.MAV.cs.verticalspeed;
                        hud1.vibex = MainV2.comPort.MAV.cs.vibex;
                        hud1.vibey = MainV2.comPort.MAV.cs.vibey;
                        hud1.vibez = MainV2.comPort.MAV.cs.vibez;
                        hud1.wpno = (int) MainV2.comPort.MAV.cs.wpno;
                        hud1.xtrack_error = MainV2.comPort.MAV.cs.xtrack_error;
                        hud1.AOA = MainV2.comPort.MAV.cs.AOA;
                        hud1.SSA = MainV2.comPort.MAV.cs.SSA;
                        hud1.critAOA = MainV2.comPort.MAV.cs.crit_AOA;
                        hud1.HoldInvalidation = false;
                        hud1.Invalidate();

                        hud1.Refresh();
                    });
                    // update map
                    if (tracklast.AddSeconds(Settings.Instance.GetDouble("FD_MapUpdateDelay", 1.2)) < DateTime.Now)
                    {
                        adsb.CurrentPosition = MainV2.comPort.MAV.cs.HomeLocation;

                        // show proximity screen
                        if (MainV2.comPort.MAV?.Proximity != null && MainV2.comPort.MAV.Proximity.DataAvailable)
                        {
                            //this.BeginInvoke((MethodInvoker)delegate { new ProximityControl(MainV2.comPort.MAV).Show(); });
                        }

                        if (Settings.Instance.GetBoolean("CHK_maprotation"))
                        {
                            // dont holdinvalidation here
                            setMapBearing();
                        }

                        if (route == null)
                        {
                            route = new GMapRoute(trackPoints, "track");
                            routes.Routes.Add(route);
                        }

                        PointLatLng currentloc = new PointLatLng(MainV2.comPort.MAV.cs.lat, MainV2.comPort.MAV.cs.lng);

                        gMapControl1.HoldInvalidation = true;

                        int numTrackLength = Settings.Instance.GetInt32("NUM_tracklength", 200);
                        // maintain route history length
                        if (route.Points.Count > numTrackLength)
                        {
                            route.Points.RemoveRange(0,
                                route.Points.Count - numTrackLength);
                        }

                        // add new route point
                        if (MainV2.comPort.MAV.cs.lat != 0 && MainV2.comPort.MAV.cs.lng != 0)
                        {
                            route.Points.Add(currentloc);
                        }

                        updateRoutePosition();

                        // update programed wp course
                        if (waypoints.AddSeconds(5) < DateTime.Now)
                        {
                            //Console.WriteLine("Doing FD WP's");
                            updateClearMissionRouteMarkers();

                            var wps = MainV2.comPort.MAV.wps.Values.ToList();
                            if (wps.Count >= 1)
                            {
                                var homeplla = new PointLatLngAlt(MainV2.comPort.MAV.cs.HomeLocation.Lat,
                                    MainV2.comPort.MAV.cs.HomeLocation.Lng,
                                    MainV2.comPort.MAV.cs.HomeLocation.Alt / CurrentState.multiplieralt, "H");

                                var overlay = new WPOverlay();

                                {
                                    List<Locationwp> mission_items;
                                    mission_items = MainV2.comPort.MAV.wps.Values.Select(a => (Locationwp) a).ToList();
                                    mission_items.RemoveAt(0);

                                    if (wps.Count == 1)
                                    {
                                        overlay.CreateOverlay(homeplla,
                                            mission_items,
                                            0 / CurrentState.multiplieralt, 0 / CurrentState.multiplieralt);
                                    }
                                    else
                                    {
                                        overlay.CreateOverlay(homeplla,
                                            mission_items,
                                            0 / CurrentState.multiplieralt, 0 / CurrentState.multiplieralt);

                                    }
                                }

                                var existing = gMapControl1.Overlays.Where(a => a.Id == overlay.overlay.Id).ToList();
                                foreach (var b in existing)
                                {
                                    gMapControl1.Overlays.Remove(b);
                                }

                                gMapControl1.Overlays.Insert(1, overlay.overlay);

                                overlay.overlay.ForceUpdate();

                                //distanceBar1.ClearWPDist();

                                var i = -1;
                                var travdist = 0.0;
                                var lastplla = overlay.pointlist.First();
                                foreach (var plla in overlay.pointlist)
                                {
                                    i++;
                                    if (plla == null)
                                        continue;

                                    var dist = lastplla.GetDistance(plla);

                                    //distanceBar1.AddWPDist((float)dist);

                                    if (i <= MainV2.comPort.MAV.cs.wpno)
                                    {
                                        travdist += dist;
                                    }
                                }

                                travdist -= MainV2.comPort.MAV.cs.wp_dist;

                                //if (MainV2.comPort.MAV.cs.mode.ToUpper() == "AUTO")
                                //distanceBar1.traveleddist = (float)travdist;
                            }

                            RegeneratePolygon();

                            // update rally points

                            rallypointoverlay.Markers.Clear();

                            foreach (var mark in MainV2.comPort.MAV.rallypoints.Values)
                            {
                                rallypointoverlay.Markers.Add(new GMapMarkerRallyPt(new PointLatLngAlt(mark)));
                            }

                            geofence.Clear();

                            var fenceoverlay = new WPOverlay();
                            fenceoverlay.overlay.Id = "fence";

                            fenceoverlay.CreateOverlay(PointLatLngAlt.Zero,
                                MainV2.comPort.MAV.fencepoints.Values.Select(a => (Locationwp) a).ToList(), 0, 0);

                            var fence = mymap.Overlays.Where(a => a.Id == "fence");
                            if (fence.Count() > 0)
                                mymap.Overlays.Remove(fence.First());
                            mymap.Overlays.Add(fenceoverlay.overlay);

                            fenceoverlay.overlay.ForceUpdate();

                            // optional on Flight data
                            if (MainV2.ShowAirports)
                            {
                                // airports
                                foreach (var item in Airports.getAirports(gMapControl1.Position).ToArray())
                                {
                                    try
                                    {
                                        rallypointoverlay.Markers.Add(new GMapMarkerAirport(item)
                                        {
                                            ToolTipText = item.Tag,
                                            ToolTipMode = MarkerTooltipMode.OnMouseOver
                                        });
                                    }
                                    catch (Exception e)
                                    {
                                        log.Error(e);
                                    }
                                }
                            }

                            waypoints = DateTime.Now;
                        }

                        updateClearRoutesMarkers();

                        // add this after the mav icons are drawn
                        if (MainV2.comPort.MAV.cs.MovingBase != null &&
                            MainV2.comPort.MAV.cs.MovingBase == PointLatLngAlt.Zero)
                        {
                            addMissionRouteMarker(new GMarkerGoogle(currentloc, GMarkerGoogleType.blue_dot)
                            {
                                Position = MainV2.comPort.MAV.cs.MovingBase,
                                ToolTipText = "Moving Base",
                                ToolTipMode = MarkerTooltipMode.OnMouseOver
                            });
                        }

                        // add gimbal point center
                        try
                        {
                            if (MainV2.comPort.MAV.param.ContainsKey("MNT_STAB_TILT")
                                && MainV2.comPort.MAV.param.ContainsKey("MNT_STAB_ROLL")
                                && MainV2.comPort.MAV.param.ContainsKey("MNT_TYPE"))
                            {
                                float temp1 = (float) MainV2.comPort.MAV.param["MNT_STAB_TILT"];
                                float temp2 = (float) MainV2.comPort.MAV.param["MNT_STAB_ROLL"];

                                float temp3 = (float) MainV2.comPort.MAV.param["MNT_TYPE"];

                                if (MainV2.comPort.MAV.param.ContainsKey("MNT_STAB_PAN") &&
                                    // (float)MainV2.comPort.MAV.param["MNT_STAB_PAN"] == 1 &&
                                    ((float) MainV2.comPort.MAV.param["MNT_STAB_TILT"] == 1 &&
                                     (float) MainV2.comPort.MAV.param["MNT_STAB_ROLL"] == 0) ||
                                    (float) MainV2.comPort.MAV.param["MNT_TYPE"] == 4) // storm driver
                                {
                                    var marker = GimbalPoint.ProjectPoint(MainV2.comPort);

                                    if (marker != PointLatLngAlt.Zero)
                                    {
                                        MainV2.comPort.MAV.cs.GimbalPoint = marker;

                                        addMissionRouteMarker(new GMarkerGoogle(marker, GMarkerGoogleType.blue_dot)
                                        {
                                            ToolTipText = "Camera Target\n" + marker,
                                            ToolTipMode = MarkerTooltipMode.OnMouseOver
                                        });
                                    }
                                }
                            }


                            // cleanup old - no markers where added, so remove all old 
                            if (MainV2.comPort.MAV.camerapoints.Count < photosoverlay.Markers.Count)
                                photosoverlay.Markers.Clear();

                            var min_interval = 0.0;
                            if (MainV2.comPort.MAV.param.ContainsKey("CAM_MIN_INTERVAL"))
                                min_interval = MainV2.comPort.MAV.param["CAM_MIN_INTERVAL"].Value / 1000.0;

                            // set fov's based on last grid calc
                            if (Settings.Instance["camera_fovh"] != null)
                            {
                                GMapMarkerPhoto.hfov = Settings.Instance.GetDouble("camera_fovh");
                                GMapMarkerPhoto.vfov = Settings.Instance.GetDouble("camera_fovv");
                            }

                            // add new - populate camera_feedback to map
                            double oldtime = double.MinValue;
                            foreach (var mark in MainV2.comPort.MAV.camerapoints.ToArray())
                            {
                                var timesincelastshot = (mark.time_usec / 1000.0) / 1000.0 - oldtime;
                                MainV2.comPort.MAV.cs.timesincelastshot = timesincelastshot;
                                bool contains = photosoverlay.Markers.Any(p => p.Tag.Equals(mark.time_usec));
                                if (!contains)
                                {
                                    if (timesincelastshot < min_interval)
                                        addMissionPhotoMarker(new GMapMarkerPhoto(mark, true));
                                    else
                                        addMissionPhotoMarker(new GMapMarkerPhoto(mark, false));
                                }

                                oldtime = (mark.time_usec / 1000.0) / 1000.0;
                            }

                            var GMapMarkerOverlapCount = new GMapMarkerOverlapCount(PointLatLng.Empty);

                            // age current
                            int camcount = MainV2.comPort.MAV.camerapoints.Count;
                            int a = 0;
                            foreach (var mark in photosoverlay.Markers)
                            {
                                if (mark is GMapMarkerPhoto)
                                {
                                    if (CameraOverlap)
                                    {
                                        var marker = ((GMapMarkerPhoto) mark);
                                        // abandon roll higher than 25 degrees
                                        if (Math.Abs(marker.Roll) < 25)
                                        {
                                            GMapMarkerOverlapCount.Add(
                                                ((GMapMarkerPhoto) mark).footprintpoly);
                                        }
                                    }

                                    if (a < (camcount - 4))
                                        ((GMapMarkerPhoto) mark).drawfootprint = false;
                                }

                                a++;
                            }

                            if (CameraOverlap)
                            {
                                if (!kmlpolygons.Markers.Contains(GMapMarkerOverlapCount) &&
                                    camcount > 0)
                                {
                                    kmlpolygons.Markers.Clear();
                                    kmlpolygons.Markers.Add(GMapMarkerOverlapCount);
                                }
                            }
                            else if (kmlpolygons.Markers.Contains(GMapMarkerOverlapCount))
                            {
                                kmlpolygons.Markers.Clear();
                            }
                        }
                        catch (Exception ex)
                        {
                            log.Error(ex);
                        }

                        lock (MainV2.instance.adsblock)
                        {
                            foreach (adsb.PointLatLngAltHdg plla in MainV2.instance.adsbPlanes.Values)
                            {
                                // 30 seconds history
                                if (((DateTime) plla.Time) > DateTime.Now.AddSeconds(-30))
                                {
                                    var adsbplane = new GMapMarkerADSBPlane(plla, plla.Heading)
                                    {
                                        ToolTipText = "ICAO: " + plla.Tag + "\n" +
                                                      "Alt: " + plla.Alt.ToString("0") + "\n" +
                                                      "Speed: " + plla.Speed.ToString("0") + "\n" +
                                                      "Heading: " + plla.Heading.ToString("0"),
                                        ToolTipMode = MarkerTooltipMode.OnMouseOver,
                                        Tag = plla
                                    };

                                    if (plla.DisplayICAO)
                                        adsbplane.ToolTipMode = MarkerTooltipMode.Always;

                                    switch (plla.ThreatLevel)
                                    {
                                        case MAVLink.MAV_COLLISION_THREAT_LEVEL.NONE:
                                            adsbplane.AlertLevel = GMapMarkerADSBPlane.AlertLevelOptions.Green;
                                            break;
                                        case MAVLink.MAV_COLLISION_THREAT_LEVEL.LOW:
                                            adsbplane.AlertLevel = GMapMarkerADSBPlane.AlertLevelOptions.Orange;
                                            break;
                                        case MAVLink.MAV_COLLISION_THREAT_LEVEL.HIGH:
                                            adsbplane.AlertLevel = GMapMarkerADSBPlane.AlertLevelOptions.Red;
                                            break;
                                    }

                                    addMissionRouteMarker(adsbplane);
                                }
                            }
                        }


                        if (route.Points.Count > 0)
                        {
                            // add primary route icon

                            // draw guide mode point for only main mav
                            if (MainV2.comPort.MAV.cs.mode.ToLower() == "guided" &&
                                MainV2.comPort.MAV.GuidedMode.x != 0)
                            {
                                addpolygonmarker("Guided Mode", MainV2.comPort.MAV.GuidedMode.y / 1e7,
                                    MainV2.comPort.MAV.GuidedMode.x / 1e7, (int) MainV2.comPort.MAV.GuidedMode.z,
                                    Color.Blue,
                                    routes);
                            }

                            // draw all icons for all connected mavs
                            foreach (var port in MainV2.Comports.ToArray())
                            {
                                // draw the mavs seen on this port
                                foreach (var MAV in port.MAVlist)
                                {
                                    var marker = Common.getMAVMarker(MAV);

                                    if (marker.Position.Lat == 0 && marker.Position.Lng == 0)
                                        continue;

                                    addMissionRouteMarker(marker);
                                }
                            }

                            if (route.Points.Count == 0 || route.Points[route.Points.Count - 1].Lat != 0 &&
                                (mapupdate.AddSeconds(3) < DateTime.Now))
                            {
                                updateMapPosition(currentloc);
                                mapupdate = DateTime.Now;
                            }

                            if (route.Points.Count == 1 && gMapControl1.Zoom == 3) // 3 is the default load zoom
                            {
                                updateMapPosition(currentloc);
                                updateMapZoom(17);
                            }
                        }

                        prop.Update(MainV2.comPort.MAV.cs.HomeLocation, MainV2.comPort.MAV.cs.Location,
                            MainV2.comPort.MAV.cs.battery_kmleft);

                        prop.alt = MainV2.comPort.MAV.cs.alt;
                        prop.altasl = MainV2.comPort.MAV.cs.altasl;
                        prop.center = gMapControl1.Position;

                        gMapControl1.HoldInvalidation = false;

                        if (gMapControl1.Visible)
                        {
                            gMapControl1.Invalidate();
                        }

                        tracklast = DateTime.Now;
                    }
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                    Console.WriteLine("FD Main loop exception " + ex);
                }
            }

            Console.WriteLine("FD Main loop exit");
        }

        /*        void NoFly_NoFlyEvent(object sender, NoFly.NoFly.NoFlyEventArgs e)
        {
            Invoke((Action)delegate
           {
               foreach (var poly in e.NoFlyZones.Polygons)
               {
                   kmlpolygons.Polygons.Add(poly);
               }
           });
        }
        */


        private void PinchGestureRecognizer_OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
        {

        }

        /// <summary>
        /// used to redraw the polygon
        /// </summary>
        void RegeneratePolygon()
        {
            List<PointLatLng> polygonPoints = new List<PointLatLng>();

            if (routes == null)
                return;

            foreach (GMapMarker m in polygons.Markers)
            {
                if (m is GMapMarkerRect)
                {
                    m.Tag = polygonPoints.Count;
                    polygonPoints.Add(m.Position);
                }
            }

            if (polygonPoints.Count < 2)
                return;

            GMapRoute homeroute = new GMapRoute("homepath");
            homeroute.Stroke = new Pen(Color.Yellow, 2);
            homeroute.Stroke.DashStyle = DashStyle.Dash;
            // add first point past home
            homeroute.Points.Add(polygonPoints[1]);
            // add home location
            homeroute.Points.Add(polygonPoints[0]);
            // add last point
            homeroute.Points.Add(polygonPoints[polygonPoints.Count - 1]);

            GMapRoute wppath = new GMapRoute("wp path");
            wppath.Stroke = new Pen(Color.Yellow, 4);
            wppath.Stroke.DashStyle = DashStyle.Custom;

            for (int a = 1; a < polygonPoints.Count; a++)
            {
                wppath.Points.Add(polygonPoints[a]);
            }

            Invoke((Action) delegate
            {
                polygons.Routes.Add(homeroute);
                polygons.Routes.Add(wppath);
            });
        }

        private void setMapBearing()
        {
            Invoke((Action) delegate { gMapControl1.Bearing = (int) ((MainV2.comPort.MAV.cs.yaw + 360) % 360); });
        }

        private async void setMJPEGSourceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string url = Settings.Instance["mjpeg_url"] != null
                ? Settings.Instance["mjpeg_url"]
                : @"http://127.0.0.1:56781/map.jpg";

            if ((url = await InputBox.Show("Mjpeg url", "Enter the url to the mjpeg source url")) != null)
            {
                Settings.Instance["mjpeg_url"] = url;

                CaptureMJPEG.Stop();

                CaptureMJPEG.URL = url;

                CaptureMJPEG.runAsync();
            }
            else
            {
                CaptureMJPEG.Stop();
            }
        }

        bool setupPropertyInfo(ref PropertyInfo input, string name, object source)
        {
            Type test = source.GetType();

            foreach (var field in test.GetProperties())
            {
                if (field.Name == name)
                {
                    input = field;
                    return true;
                }
            }

            return false;
        }


        private void SkglView_OnTouch(object sender, SKTouchEventArgs e)
        {
            touchpoint = e.Location;
            //SkglView.InvalidateSurface();

            e.Handled = true;
        }

        DateTime lastscreenupdate = DateTime.Now;
        volatile int updateBindingSourcecount;

        object updateBindingSourcelock = new object();
        string updateBindingSourceThreadName = "";
        private string modeselected;

        void tfr_GotTFRs(object sender, EventArgs e)
        {
            Invoke((Action) delegate
            {
                foreach (var item in tfr.tfrs)
                {
                    List<List<PointLatLng>> points = item.GetPaths();

                    foreach (var list in points)
                    {
                        GMapPolygon poly = new GMapPolygon(list, item.NAME);

                        poly.Fill = new SolidBrush(Color.FromArgb(30, Color.Blue));

                        tfrpolygons.Polygons.Add(poly);
                    }
                }

                tfrpolygons.IsVisibile = MainV2.ShowTFR;
            });
        }

        private void tracklog_Scroll(object sender, EventArgs e)
        {
            try
            {
                BUT_clear_track_Click(sender, e);

                MainV2.comPort.lastlogread = DateTime.MinValue;
                MainV2.comPort.MAV.cs.ResetInternals();

                if (MainV2.comPort.logplaybackfile != null)
                    MainV2.comPort.logplaybackfile.BaseStream.Position =
                        (long) (MainV2.comPort.logplaybackfile.BaseStream.Length * (tracklog.Value / 100.0));

                updateLogPlayPosition(false);
            }
            catch
            {
            } // ignore any invalid 
        }


        private void updateBindingSource()
        {
            //  run at 25 hz.
            if (lastscreenupdate.AddMilliseconds(40) < DateTime.Now)
            {
                lock (updateBindingSourcelock)
                {
                    // this is an attempt to prevent an invoke queue on the binding update on slow machines
                    if (updateBindingSourcecount > 0)
                    {
                        if (lastscreenupdate < DateTime.Now.AddSeconds(-5))
                        {
                            updateBindingSourcecount = 0;
                        }

                        return;
                    }

                    updateBindingSourcecount++;
                    updateBindingSourceThreadName = Thread.CurrentThread.Name;
                }

                Forms.Device.BeginInvokeOnMainThread((Action) delegate
                {
                    //updateBindingSourceWork();

                    lock (updateBindingSourcelock)
                    {
                        updateBindingSourcecount--;
                    }
                });
            }
        }


        // to prevent cross thread calls while in a draw and exception
        private void updateClearMissionRouteMarkers()
        {
            // not async
            Invoke((Action) delegate
            {
                polygons.Routes.Clear();
                polygons.Markers.Clear();
                routes.Markers.Clear();
            });
        }

        // to prevent cross thread calls while in a draw and exception
        private void updateClearRoutes()
        {
            // not async
            Invoke((Action) delegate
            {
                routes.Routes.Clear();
                routes.Routes.Add(route);
            });
        }

        private void updateClearRoutesMarkers()
        {
            Invoke((Action) delegate { routes.Markers.Clear(); });
        }

        private void updateLogPlayPosition(bool updatetracklog = true)
        {
            BeginInvoke((Action) delegate
            {
                try
                {
                    if (updatetracklog && tracklog.IsVisible)
                    {
                        // prevent event fire
                        tracklog.ValueChanged -= tracklog_Scroll;
                        tracklog.Value = (int) (MainV2.comPort.logplaybackfile.BaseStream.Position /
                                                (double) MainV2.comPort.logplaybackfile.BaseStream.Length * 100);
                        tracklog.ValueChanged += tracklog_Scroll;
                    }

                    if (lbl_logpercent.IsVisible)
                        lbl_logpercent.Text =
                            (MainV2.comPort.logplaybackfile.BaseStream.Position /
                             (double) MainV2.comPort.logplaybackfile.BaseStream.Length).ToString("0.00%");

                    if (lbl_playbackspeed.IsVisible)
                        lbl_playbackspeed.Text = "x " + LogPlayBackSpeed;
                }
                catch
                {
                }
            });
        }

        private void updateMapPosition(PointLatLng currentloc)
        {
            Invoke((Action) delegate
            {
                try
                {
                    if (lastmapposchange.Second != DateTime.Now.Second)
                    {
                        if (Math.Abs(currentloc.Lat - gMapControl1.Position.Lat) > 0.0001 ||
                            Math.Abs(currentloc.Lng - gMapControl1.Position.Lng) > 0.0001)
                        {
                            gMapControl1.Position = currentloc;
                        }

                        lastmapposchange = DateTime.Now;
                    }

                    //hud1.Refresh();
                }
                catch
                {
                }
            });
        }

        private void updateMapZoom(int zoom)
        {
            Invoke((Action) delegate
            {
                try
                {
                    gMapControl1.Zoom = zoom;
                }
                catch
                {
                }
            });
        }

        void UpdateOverlayVisibility()
        {
            // change overlay visability
            if (gMapControl1.ViewArea != null)
            {
                var bounds = gMapControl1.ViewArea;
                bounds.Inflate(1, 1);

                if (kmlpolygons == null)
                    return;

                foreach (var poly in kmlpolygons.Polygons)
                {
                    if (bounds.Contains(poly.Points[0]))
                        poly.IsVisible = true;
                    else
                        poly.IsVisible = false;
                }
            }
        }

        private void updatePlayPauseButton(bool playing)
        {
            if (playing)
            {
                if (BUT_playlog.Text == "Pause")
                    return;

                BeginInvoke((Action) delegate
                {
                    try
                    {
                        BUT_playlog.Text = "Pause";
                    }
                    catch
                    {
                    }
                });
            }
            else
            {
                if (BUT_playlog.Text == "Play")
                    return;

                BeginInvoke((Action) delegate
                {
                    try
                    {
                        BUT_playlog.Text = "Play";
                    }
                    catch
                    {
                    }
                });
            }
        }

        private void BeginInvoke(Action action)
        {
            Forms.Device.BeginInvokeOnMainThread(action);
        }

        private void updateRoutePosition()
        {
            // not async
            Invoke((Action) delegate { gMapControl1.UpdateRouteLocalPosition(route); });
        }

        private void Button_Onclicked(object sender, EventArgs e)
        {
            
        }

        private MAVLinkInterface mav => MainV2.comPort;

        private async void Arm_OnClicked(object sender, EventArgs e)
        {
            try
            {
                await MainV2.comPort.doARMAsync(MainV2.comPort.MAV.sysid, MainV2.comPort.MAV.compid, true);
            }
            catch (Exception exception)
            {
                UserDialogs.Instance.Toast(exception.Message, TimeSpan.FromSeconds(3));
                Console.WriteLine(exception);
                //throw;
            }
        }

        private async void Disarm_OnClicked(object sender, EventArgs e)
        {
            try
            {
                await mav.doARMAsync(mav.MAV.sysid, mav.MAV.compid, false);
            }
            catch (Exception exception)
            {
                UserDialogs.Instance.Toast(exception.Message, TimeSpan.FromSeconds(3));
                Console.WriteLine(exception);
               // throw;
            }
        }

        private void Set_Mode_OnClicked(object sender, EventArgs e)
        {
            mav.setMode(mav.MAV.sysid, mav.MAV.compid, modeselected);
        }

        private async void Get_Mission_OnClicked(object sender, EventArgs e)
        {
            try
            {
                await mav_mission.download(mav, mav.MAV.sysid, mav.MAV.compid, MAVLink.MAV_MISSION_TYPE.MISSION);
            }
            catch (Exception exception)
            {
                UserDialogs.Instance.Toast(exception.Message, TimeSpan.FromSeconds(3));
                Console.WriteLine(exception);
                //throw;
            }
        }

        private async void Get_Fence_OnClicked(object sender, EventArgs e)
        {
            try
            {
                await mav_mission.download(mav, mav.MAV.sysid, mav.MAV.compid, MAVLink.MAV_MISSION_TYPE.FENCE);
            }
            catch (Exception exception)
            {
                UserDialogs.Instance.Toast(exception.Message, TimeSpan.FromSeconds(3));
                Console.WriteLine(exception);
                //throw;
            }
        }

        private async void Get_Rally_OnClicked(object sender, EventArgs e)
        {
            try
            {
                await mav_mission.download(mav, mav.MAV.sysid, mav.MAV.compid, MAVLink.MAV_MISSION_TYPE.RALLY);
            }
            catch (Exception exception)
            {
                UserDialogs.Instance.Toast(exception.Message, TimeSpan.FromSeconds(3));
                Console.WriteLine(exception);
                //throw;
            }
        }

        private async void Takeoff___2m_OnClicked(object sender, EventArgs e)
        {
            try
            {
                mav.setMode("GUIDED"); 
                await mav.doCommandAsync(mav.MAV.sysid, mav.MAV.compid, MAVLink.MAV_CMD.TAKEOFF, 0, 0, 0, 0, 0, 0, 2);
            }
            catch (Exception exception)
            {
                UserDialogs.Instance.Toast(exception.Message, TimeSpan.FromSeconds(3));
                Console.WriteLine(exception);
                //throw;
            }
        }

        private void Mode_OnSelectedIndexChanged(object sender, EventArgs e)
        {
            modeselected = Mode.SelectedItem.ToString();
        }
    }

    internal class InputBox
    {
        public static async Task<string> Show(string mjpegUrl, string enterTheUrlToTheMjpegSourceUrl)
        {
            var result = await InputBox1(mjpegUrl, enterTheUrlToTheMjpegSourceUrl, MainPage.Instance.Navigation);
            return result;
        }

        public static Task<string> InputBox1(string title, string description, INavigation navigation)
        {
            // wait in this proc, until user did his input 
            var tcs = new TaskCompletionSource<string>();

            var lblTitle = new Label
                {Text = title, HorizontalOptions = LayoutOptions.Center, FontAttributes = FontAttributes.Bold};
            var lblMessage = new Label {Text = description};
            var txtInput = new Entry {Text = ""};

            var btnOk = new Button
            {
                Text = "Ok",
                WidthRequest = 100,
                //BackgroundColor = Color.FromRgb(0.8, 0.8, 0.8),
            };
            btnOk.Clicked += async (s, e) =>
            {
                // close page
                var result = txtInput.Text;
                await navigation.PopModalAsync();
                // pass result
                tcs.SetResult(result);
            };

            var btnCancel = new Button
            {
                Text = "Cancel",
                WidthRequest = 100,
                //BackgroundColor = Color.FromRgb(0.8, 0.8, 0.8)
            };
            btnCancel.Clicked += async (s, e) =>
            {
                // close page
                await navigation.PopModalAsync();
                // pass empty result
                tcs.SetResult(null);
            };

            var slButtons = new StackLayout
            {
                Orientation = StackOrientation.Horizontal,
                Children = {btnOk, btnCancel},
            };

            var layout = new StackLayout
            {
                Padding = new Thickness(0, 40, 0, 0),
                VerticalOptions = LayoutOptions.StartAndExpand,
                HorizontalOptions = LayoutOptions.CenterAndExpand,
                Orientation = StackOrientation.Vertical,
                Children = {lblTitle, lblMessage, txtInput, slButtons},
            };

            // create and show page
            var page = new ContentPage();
            page.Content = layout;
            navigation.PushModalAsync(page);
            // open keyboard
            txtInput.Focus();

            // code is waiting her, until result is passed with tcs.SetResult() in btn-Clicked
            // then proc returns the result
            return tcs.Task;
        }
    }
}